package com.mashibing.service.impl;

import com.mashibing.internalcommon.constant.CommonStatusEnum;
import com.mashibing.internalcommon.constant.DriverCarConstants;
import com.mashibing.internalcommon.constant.IdentityConstants;
import com.mashibing.internalcommon.constant.TokenConstants;
import com.mashibing.internalcommon.dto.ResponseResult;
import com.mashibing.internalcommon.remote.DriverUserClient;
import com.mashibing.internalcommon.request.VerificationCodeDTO;
import com.mashibing.internalcommon.response.DriverUserExistsResponse;
import com.mashibing.internalcommon.response.NumberCodeResponse;
import com.mashibing.internalcommon.response.TokenResponse;
import com.mashibing.internalcommon.util.JwtUtils;
import com.mashibing.internalcommon.util.RedisPrefixUtils;
import com.mashibing.remote.ServicePassengerUserClient;
import com.mashibing.remote.ServiceVerificationCodeClient;
import com.mashibing.requset.CheckVerificationCodeDTO;
import com.mashibing.service.VerificationCodeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class VerificationCodeServiceImpl implements VerificationCodeService {

    @Autowired
    private ServiceVerificationCodeClient codeClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ServicePassengerUserClient servicePassengerUserClient;

    @Autowired
    private DriverUserClient driverUserClient;

    @Override
    public ResponseResult<Void> generatorCode(CheckVerificationCodeDTO verificationCodeDTO) {
        // 获取验证码
        ResponseResult<NumberCodeResponse> numberCode = codeClient.getNumberCode(6);
        String code = numberCode.getData().getNumberCode();
        System.out.println("手机号：" + verificationCodeDTO.getPhone() + "  获取验证码成功:" + code);

        // 放入redis
        String key = RedisPrefixUtils.generatorKeyByPhone(verificationCodeDTO.getPhone(), verificationCodeDTO.getIdentity());
        redisTemplate.opsForValue().set(key, code, 2, TimeUnit.MINUTES);
        System.out.println("放入redis成功");

        return ResponseResult.success();
    }

    @Override
    public ResponseResult checkVerificationCode(CheckVerificationCodeDTO verificationCodeDTO) {

        String phone = verificationCodeDTO.getPhone();
        String identity = verificationCodeDTO.getIdentity();
        String verificationCode = verificationCodeDTO.getVerificationCode();

        if (IdentityConstants.DRIVER_IDENTITY.equals(identity)) {
            log.info("司机电话号码为：" + phone);

            // 调用feign远程服务，验证电话的有效性
            ResponseResult<DriverUserExistsResponse> result = driverUserClient.verificationDriverUser(phone);
            int ifExist = result.getData().getIfExist();
            if (ifExist == DriverCarConstants.DRIVER_NOT_EXISTS) {
                return ResponseResult.fail(CommonStatusEnum.DRIVER_NOT_EXITST.getCode(),CommonStatusEnum.DRIVER_NOT_EXITST.getMessage(),null);
            }
        }

        // 查询redis
        String key = RedisPrefixUtils.generatorKeyByPhone(phone, identity);
        String redisCode = redisTemplate.opsForValue().get(key);
        System.out.println("redis中的验证码为：" + redisCode);

        // 验证验证码
        if (StringUtils.isBlank(redisCode) || !redisCode.equals(verificationCode)) {
            return ResponseResult.fail(CommonStatusEnum.VERIFICATION_CODE_ERROR.getCode(), CommonStatusEnum.VERIFICATION_CODE_ERROR.getMessage(), null);
        }

        // 查看用户是否存在,不存在则新增数据
        if (identity.equals(IdentityConstants.PASSENGER_IDENTITY)) {
            VerificationCodeDTO codeDTO = new VerificationCodeDTO();
            BeanUtils.copyProperties(verificationCodeDTO, codeDTO);
            codeDTO.setPassengerPhone(phone);
            try {
                servicePassengerUserClient.loginOrRegister(codeDTO);
            } catch (Exception e) {
                log.error("用户新增发生错误",e);
                return ResponseResult.fail(CommonStatusEnum.CALL_USER_ADD_ERROR.getCode(),CommonStatusEnum.CALL_USER_ADD_ERROR.getMessage(),null);
            }
        }

        // 生成accessToken和refreshToken
        String accessToken = JwtUtils.generatorToken(phone, identity, TokenConstants.ACCESS_TOKEN_TYPE);
        String refreshToken = JwtUtils.generatorToken(phone, identity, TokenConstants.REFRESH_TOKEN_TYPE);

        // 开启redis事务
        redisTemplate.setEnableTransactionSupport(true);

        SessionCallback<Boolean> sessionCallback = new SessionCallback<Boolean>() {
            @Override
            public Boolean execute(RedisOperations operations) throws DataAccessException {
                // 事务开始
                redisTemplate.multi();
                try {
                    // accessToken放入redis
                    String redisAccessTokenKey = RedisPrefixUtils.generatorTokenKey(phone,identity, TokenConstants.ACCESS_TOKEN_TYPE);
                    operations.opsForValue().set(redisAccessTokenKey, accessToken, 30, TimeUnit.DAYS);

                    // refreshToken放入redis
                    String redisRefreshTokenKey = RedisPrefixUtils.generatorTokenKey(phone, identity, TokenConstants.REFRESH_TOKEN_TYPE);
                    operations.opsForValue().set(redisRefreshTokenKey, refreshToken, 31, TimeUnit.DAYS);
                    operations.exec();
                    return true;
                } catch (Exception e) {
                    operations.discard();
                    return false;
                }
            }
        };
        Boolean execute = redisTemplate.execute(sessionCallback);
        System.out.println("事务提交or回滚：" + execute);
        if (Boolean.TRUE.equals(execute)) {
            // 删除验证码
            redisTemplate.delete(key);

            TokenResponse tokenResponse = new TokenResponse();
            tokenResponse.setAccessToken(accessToken);
            tokenResponse.setRefreshToken(refreshToken);
            return ResponseResult.success(tokenResponse);
        }
        return ResponseResult.fail(CommonStatusEnum.CHECK_CODE_ERROR.getCode(), CommonStatusEnum.CHECK_CODE_ERROR.getMessage(), null);
    }

}
